package ingress

import (
	"context"
	"fmt"
	v12 "k8s.io/api/networking/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	v13 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"
	ctrl "sigs.k8s.io/controller-runtime"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/log"
	appv1beta1 "taptap.com/app/api/v1beta1"
)

func ReconcileIngress(ctx context.Context, c client.Client, req ctrl.Request, app *appv1beta1.App) error {
	if len(app.Spec.Ingresses) == 0 {
		return nil
	}

	logger := log.FromContext(ctx)
	for _, item := range app.Spec.Ingresses {
		ingress := &v12.Ingress{}
		err := c.Get(ctx, types.NamespacedName{
			Namespace: app.Namespace,
			Name:      fmt.Sprintf("%s-%s", app.Name, item.Name),
		}, ingress)
		e, okay := err.(*errors.StatusError)
		if err != nil && (!okay || e.ErrStatus.Code != 404) {
			return err
		}

		ingress.Name = fmt.Sprintf("%s-%s", app.Name, item.Name)
		ingress.Namespace = app.Namespace
		ingress.OwnerReferences = []v13.OwnerReference{
			{
				APIVersion: app.APIVersion,
				Kind:       app.Kind,
				Name:       app.Name,
				UID:        app.UID,
			},
		}
		ingress.Spec.Rules = make([]v12.IngressRule, 0)
		for _, route := range item.Routes {
			hTTPIngressRuleValue := &v12.HTTPIngressRuleValue{}
			for _, path := range route.Paths {
				pathType := v12.PathTypeExact
				hTTPIngressRuleValue.Paths = append(hTTPIngressRuleValue.Paths, v12.HTTPIngressPath{
					Path:     path,
					PathType: &pathType,
					Backend: v12.IngressBackend{
						Service: &v12.IngressServiceBackend{
							Name: app.Name,
							Port: v12.ServiceBackendPort{
								Number: item.ServicePort,
							},
						},
					},
				})
			}

			for _, host := range route.Hosts {
				ingress.Spec.Rules = append(ingress.Spec.Rules, v12.IngressRule{
					Host: host,
					IngressRuleValue: v12.IngressRuleValue{
						HTTP: hTTPIngressRuleValue,
					},
				})
			}
		}

		if e != nil && e.ErrStatus.Code == 404 {
			err = c.Create(ctx, ingress)
			if err != nil {
				logger.Error(err, fmt.Sprintf("create ingress: %s failed", ingress.Name))
				return err
			}
			continue
		}

		err = c.Update(ctx, ingress)
		if err != nil {
			logger.Error(err, fmt.Sprintf("update ingress: %s failed", ingress.Name))
			return err
		}
	}

	return nil
}
